{
  "cells": [
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "%matplotlib inline"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "\nPyTorch: Tensors\n----------------\n\nA third order polynomial, trained to predict $y=\\sin(x)$ from $-\\pi$\nto $pi$ by minimizing squared Euclidean distance.\n\nThis implementation uses PyTorch tensors to manually compute the forward pass,\nloss, and backward pass.\n\nA PyTorch Tensor is basically the same as a numpy array: it does not know\nanything about deep learning or computational graphs or gradients, and is just\na generic n-dimensional array to be used for arbitrary numeric computation.\n\nThe biggest difference between a numpy array and a PyTorch Tensor is that\na PyTorch Tensor can run on either CPU or GPU. To run operations on the GPU,\njust cast the Tensor to a cuda datatype.\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "import torch\nimport math\n\n\ndtype = torch.float\ndevice = torch.device(\"cpu\")\n# device = torch.device(\"cuda:0\") # Uncomment this to run on GPU\n\n# Create random input and output data\nx = torch.linspace(-math.pi, math.pi, 2000, device=device, dtype=dtype)\ny = torch.sin(x)\n\n# Randomly initialize weights\na = torch.randn((), device=device, dtype=dtype)\nb = torch.randn((), device=device, dtype=dtype)\nc = torch.randn((), device=device, dtype=dtype)\nd = torch.randn((), device=device, dtype=dtype)\n\nlearning_rate = 1e-6\nfor t in range(2000):\n    # Forward pass: compute predicted y\n    y_pred = a + b * x + c * x ** 2 + d * x ** 3\n\n    # Compute and print loss\n    loss = (y_pred - y).pow(2).sum().item()\n    if t % 100 == 99:\n        print(t, loss)\n\n    # Backprop to compute gradients of a, b, c, d with respect to loss\n    grad_y_pred = 2.0 * (y_pred - y)\n    grad_a = grad_y_pred.sum()\n    grad_b = (grad_y_pred * x).sum()\n    grad_c = (grad_y_pred * x ** 2).sum()\n    grad_d = (grad_y_pred * x ** 3).sum()\n\n    # Update weights using gradient descent\n    a -= learning_rate * grad_a\n    b -= learning_rate * grad_b\n    c -= learning_rate * grad_c\n    d -= learning_rate * grad_d\n\n\nprint(f'Result: y = {a.item()} + {b.item()} x + {c.item()} x^2 + {d.item()} x^3')"
      ]
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": "Python 3",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3.6.12"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}